package de.mfits.fireflyplayer.importer;

import static de.mfits.fireflyplayer.storage.DbHelper.ALBUM;
import static de.mfits.fireflyplayer.storage.DbHelper.ARTIST;
import static de.mfits.fireflyplayer.storage.DbHelper.ARTIST_ALBUM;
import static de.mfits.fireflyplayer.storage.DbHelper.DISC_NUMBER;
import static de.mfits.fireflyplayer.storage.DbHelper.GENRE;
import static de.mfits.fireflyplayer.storage.DbHelper.ID;
import static de.mfits.fireflyplayer.storage.DbHelper.PART_OF_A_COMPILATION;
import static de.mfits.fireflyplayer.storage.DbHelper.SIZE;
import static de.mfits.fireflyplayer.storage.DbHelper.TIME;
import static de.mfits.fireflyplayer.storage.DbHelper.TITLE;
import static de.mfits.fireflyplayer.storage.DbHelper.TRACK_NUMBER;
import static de.mfits.fireflyplayer.storage.DbHelper.TYPE;
import static de.mfits.fireflyplayer.storage.DbHelper.YEAR;

import java.util.Iterator;

import net.firefly.client.controller.events.SongListLoadProgressEvent;
import net.firefly.client.controller.listeners.SongListLoadProgressListener;
import net.firefly.client.model.data.Song;
import net.firefly.client.model.data.list.SongList;
import net.firefly.client.tools.FireflyClientException;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Handler;
import android.util.Log;
import de.mfits.fireflyplayer.R;
import de.mfits.fireflyplayer.storage.DbHelper;

/**
 * Imports a music library from a server into the database
 *
 * @author Michael Forster <email@michael-forster.de>
 */
public class LibraryImporter {

  /** A callback to be executed after import is finished */
  public interface FinishedCallback {
    void onFinished(LibraryImporter importer);
  }

  /** A dialog that shows the import progress. */
  private final ProgressDialog dialog;

  /** The handler for communication between the worker and UI threads. */
  private final Handler handler = new Handler();

  /** The context. */
  private final Context context;

  /** The callback to be executed after import is finished */
  private final FinishedCallback callback;

  /** An exception that occurred in the background or null */
  private Exception exception;

  public Exception getException() {
    return exception;
  }

  /**
   * Creates a new importer.
   *
   * @param context the context
   */
  public LibraryImporter(Context context) {
    this(context, null);
  }

  /**
   * Creates a new importer with a callback.
   *
   * @param context the context
   * @param callback the callback
   */
  public LibraryImporter(Context context, FinishedCallback callback) {
    this.context = context;
    this.callback = callback;

    dialog = new ProgressDialog(context);
    dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    dialog.setTitle(R.string.import_progress_title);
    dialog.setMessage(context.getText(R.string.import_progress_connecting));
  }

  /**
   * Imports a music library from a server into the database.
   *
   * @param server the server to import from
   */
  public void importLibrary(final ServerConnection server) {
    dialog.show();

    new Thread(new Runnable() {
      public void run() {
        try {
          SongList songs = loadSongs(server);
          storeSongs(songs);

          finish();
        } catch (Exception e) {
          Log.e(getClass().getSimpleName(), e.getMessage(), e);
          exception = e;
        }
      }
    }).start();
  }

  /**
   * Loads the songs from the server.
   *
   * @param server the server
   * @return the loaded songs
   * @throws FireflyClientException if an error occurred
   */
  private SongList loadSongs(final ServerConnection server) throws FireflyClientException {
    return server.getSongList(new SongListLoadProgressListener() {
      @Override
      public void onProgressChange(SongListLoadProgressEvent evt) {
        updateProgress(evt.getNumberOfLoadedSongs(), evt.getSongListSize());
      }
    });
  }

  /**
   * Stores the songs into the database.
   *
   * @param songs the somgs
   */
  private void storeSongs(final SongList songs) {
    handler.post(new Runnable() {
      @Override
      public void run() {
        dialog.setMessage(context.getText(R.string.import_progress_storing));
        dialog.setProgress(0);
        dialog.setMax(songs.size());
      }
    });

    DbHelper helper = new DbHelper(context);
    SQLiteDatabase db = helper.getWritableDatabase();

    db.beginTransaction();
    try {
      db.execSQL("delete from songs;");

      @SuppressWarnings("unchecked")
      Iterator<Song> it = songs.iterator();
      while (it.hasNext()) {
        Song song = it.next();
        ContentValues values = new ContentValues(4);
        values.put(ID, song.getDatabaseItemId());
        values.put(PART_OF_A_COMPILATION, song.isPartOfACompilation());
        values.put(GENRE, song.getGenre());
        values.put(ARTIST_ALBUM, song.getArtistAlbum());
        values.put(ARTIST, song.getArtist());
        values.put(ALBUM, song.getAlbum());
        values.put(YEAR, song.getYear());
        values.put(TITLE, song.getTitle());
        values.put(DISC_NUMBER, song.getDiscNumber());
        values.put(TRACK_NUMBER, song.getTrackNumber());
        values.put(SIZE, song.getSize());
        values.put(TIME, song.getTime());
        values.put(TYPE, song.getType());
        db.insert("songs", null, values);

        handler.post(new Runnable() {
          @Override
          public void run() {
            dialog.incrementProgressBy(1);
          }
        });
      }
      db.setTransactionSuccessful();
    } finally {
      db.endTransaction();
    }
  }

  /**
   * Updates the progress bar in the dialog.
   *
   * @param progress the current value
   * @param max the maximum value or -1 if unknown
   */
  private void updateProgress(final int progress, final int max) {
    handler.post(new Runnable() {
      @Override
      public void run() {
        dialog.setMessage(context.getText(R.string.import_progress_loading));
        if (max != -1) {
          dialog.setMax(max);
        }
        dialog.setProgress(progress);
      }
    });
  }

  /**
   * Hides the dialog and calls the callback.
   */
  private void finish() {
    handler.post(new Runnable() {
      @Override
      public void run() {
        dialog.dismiss();

        if (callback != null) {
          callback.onFinished(LibraryImporter.this);
        }
      }
    });
  }
}
